/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：实现有限状态机的主要逻辑
* CreateTime：2020-04-05 21:28:43
* Version：1.0
* Modify Recorder：
*************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;

namespace TinyToolKit.FSM
{
    public class FsmSystem<T> : FsmSystemBase,IReference, IFsmSystem<T> where T : class
    {
        private T _owner;
        private readonly Dictionary<Type, FsmState<T>> _states;
        private readonly Dictionary<string, object> _datas;
        private FsmState<T> _currentState;
        private float _currentStateTime;
        private bool _isRunning; 
        private bool _isDestroyed;

        public FsmSystem()
        {
            _owner = null;
            _states = new Dictionary<Type, FsmState<T>>();
            _datas = new Dictionary<string, object>();
            _currentState = null;
            _currentStateTime = 0;
            _isDestroyed = true;
        }

        #region FSM Base
        public override Type OwnerType => typeof(T);

        public override int FsmStateCount => _states.Count;

        public override bool IsRunning => _currentState != null && _isRunning;

        public override bool IsDestroyed => _isDestroyed;

        public override string CurrentStateName => _currentState != null ? _currentState.GetType().Name : string.Empty;

        public override float CurrentStateTime => _currentStateTime;

        public T Owner => _owner;

        public FsmState<T> CurrentState => _currentState;

        internal override void Update()
        {
            if (_currentState == null || _isRunning == false) return;

            _currentStateTime += Time.deltaTime;
            _currentState.OnAct();
        }

        public override void Pause()
        {
            if (_currentState != null && _isRunning)
                _isRunning = false;
        }

        public override void Resume()
        {
            if (_currentState != null && _isRunning == false)
                _isRunning = true;
        }

        internal override void Shutdown()
        {
            Clear();
        }

        public void Clear()
        {
            _currentState?.OnExit();

            foreach (KeyValuePair<Type, FsmState<T>> state in _states)
            {
                state.Value.OnDestroy();
            }

            Name = null;
            _owner = null;
            _states.Clear();
            _currentState = null;
            _currentStateTime = 0f;
            _isRunning = false;
            _isDestroyed = true;
        }
        #endregion

        #region Create FSM
        public bool Create(string name, T owner, params FsmState<T>[] states)
        {
            List<FsmState<T>> statesList = new List<FsmState<T>>(states);

            return Create(name, owner, statesList);
        }

        public bool Create(string name, T owner, List<FsmState<T>> states)
        {
            if (owner == null) return false;
            if (states == null || states.Count == 0) return false;

            this.Name = name;
            this._owner = owner;
            this._isRunning = false;
            this._isDestroyed = false;

            foreach (FsmState<T> state in states)
            {
                if (state != null)
                {
                    Type stateType = state.GetType();
                    if (this._states.ContainsKey(stateType) == false)
                    {
                        this._states.Add(stateType, state);
                        state.SetFsmSystem(this);
                    }
                    else
                    {
                        TLogTool.Info("{0}中已经包含了状态{1}", this.FullName, stateType.FullName);
                    }
                }
            }

            return true;
        }
        #endregion

        #region Start FSM 
        /// <summary>
        /// 开启指定的有限状态机
        /// </summary>
        /// <typeparam name="T2">需要被开启的状态类型</typeparam>
        public bool Start<T2>() where T2 : FsmState<T>
        {
            return Start(typeof(T2));
        }

        /// <summary>
        /// 开启指定的有限状态机
        /// </summary>
        /// <param name="stateType">需要被开启的状态类型</param>
        public bool Start(Type stateType)
        {
            if (stateType == null) return false;

            if (typeof(FsmState<T>).IsAssignableFrom(stateType) == false)
            {
                TLogTool.Info(stateType.FullName + "无效，不是继承自FsmState");
                return false;
            }

            if (IsRunning)
            {
                TLogTool.Info(stateType.FullName + "已经运行了，不能再启动");
                return false;
            }

            FsmState<T> state = GetState(stateType);
            if (state == null)
            {
                TLogTool.Info(stateType.FullName + "不存在，不能启动");
                return false;
            }

            foreach (FsmState<T> item in _states.Values)
            {
                item.OnInit();
            }

            _currentStateTime = 0;
            _currentState = state;
            _isRunning = true;
            _currentState.OnEnter();
            return true;
        }
        #endregion

        #region Has State
        /// <summary>
        /// 判断是否存在该状态
        /// </summary>
        /// <returns><c>true</c>, 该状态存在, <c>false</c> 不存在</returns>
        /// <typeparam name="T2">需要判断的状态类型</typeparam>
        public bool HasState<T2>() where T2 : FsmState<T>
        {
            return _states.ContainsKey(typeof(T2));
        }

        /// <summary>
        /// 判断是否存在该状态
        /// </summary>
        /// <returns><c>true</c>, 该状态存在, <c>false</c>  不存在</returns>
        /// <param name="stateType">需要判断的状态类型</param>
        public bool HasState(Type stateType)
        {
            if (stateType == null) return false;

            if (typeof(FsmState<T>).IsAssignableFrom(stateType) == false)
            {
                TLogTool.Info(stateType.FullName + "无效，不是继承自FsmState");
                return false;
            }

            return _states.ContainsKey(stateType);
        }
        #endregion

        #region Get State
        /// <summary>
        /// 获取有限状态机的状态
        /// </summary>
        /// <returns>返回该状态</returns>
        /// <typeparam name="T2">需要返回状态的类型.</typeparam>
        public T2 GetState<T2>() where T2 : FsmState<T>
        {
            return GetState(typeof(T2)) as T2;
        }

        /// <summary>
        /// 获取有限状态机的状态
        /// </summary>
        /// <returns>返回该状态</returns>
        /// <param name="stateType">需要返回状态的类型</param>
        public FsmState<T> GetState(Type stateType)
        {
            if (stateType == null) return null;

            if (typeof(FsmState<T>).IsAssignableFrom(stateType) == false)
            {
                TLogTool.Info(stateType.FullName + "无效，不是继承自FsmState");
                return null;
            }

            FsmState<T> state = null;
            if (_states.TryGetValue(stateType, out state))
            {
                return state;
            }
            return null;
        }

        /// <summary>
        /// 获取所有的状态
        /// </summary>
        /// <returns>返回该状态机所有的状态</returns>
        public FsmState<T>[] GetAllStates()
        {
            int index = 0;
            FsmState<T>[] states = new FsmState<T>[_states.Count];
            foreach (KeyValuePair<Type, FsmState<T>> state in _states)
            {
                states[index++] = state.Value;
            }
            return states;
        }

        /// <summary>
        /// 获取所有的状态，states不能为Null
        /// </summary>
        /// <param name="states">返回该状态机所有的状态</param>
        public void GetAllStates(List<FsmState<T>> states)
        {
            if (states == null) return;

            states.Clear();
            foreach (KeyValuePair<Type, FsmState<T>> state in _states)
            {
                states.Add(state.Value);
            }
        }
        #endregion

        #region Change State
        /// <summary>
        /// 切换当前有限状态机的状态
        /// </summary>
        /// <typeparam name="T2">需要切换的目标状态</typeparam>
        public void ChangeState<T2>() where T2 : FsmState<T>
        {
            ChangeState(typeof(T2));
        }

        /// <summary>
        /// 切换当前有限状态机的状态
        /// </summary>
        /// <param name="stateType">需要切换的目标状态</param>
        public void ChangeState(Type stateType)
        {
            if (_currentState == null) return;
            if (typeof(FsmState<T>).IsAssignableFrom(stateType) == false) return;

            FsmState<T> state = GetState(stateType);
            if (state == null) return;

            _currentState.OnExit();
            _currentStateTime = 0f;
            _currentState = state;
            _currentState.OnEnter();
        }
        #endregion

        #region Data
        /// <summary>
        /// 是否存在有限状态机数据
        /// </summary>
        public bool HasData(string name)
        {
            if (string.IsNullOrEmpty(name)) return false;

            return _datas.ContainsKey(name);
        }

        /// <summary>
        /// 获取有限状态机的数据
        /// </summary>
        public object GetData(string name)
        {
            if (string.IsNullOrEmpty(name)) return null;
            if (_datas.TryGetValue(name, out var data))
                return data;
            return null;
        }

        /// <summary>
        /// 获取有限状态机的数据
        /// </summary>
        public T2 GetData<T2>(string name)
        {
            object data = GetData(name);
            if (data is T2)
                return (T2)data;
            return default; 
        }

        /// <summary>
        /// 设置有限状态机数据
        /// </summary>
        /// <param name="name">数据的名称</param>
        /// <param name="data">要存储的数据</param>
        public void SetData(string name, object data)
        {
            if (string.IsNullOrEmpty(name)) return;

            _datas.Add(name, data);
        }

        /// <summary>
        /// 设置有限状态机数据
        /// </summary>
        /// <param name="name">数据的名称.</param>
        /// <param name="data">要存储的数据</param>
        /// <typeparam name="T2">要存储的数据类型</typeparam>
        public void SetData<T2>(string name, T2 data)
        {
            if (string.IsNullOrEmpty(name)) return;

            _datas.Add(name, data);
        }

        /// <summary>
        /// 移除有限状态机数据
        /// </summary>
        /// <returns><c>true</c>, 数据移除成功, <c>false</c> 移除失败</returns>
        /// <param name="name">需要移除的数据名称</param>
        public bool RemoveData(string name)
        {
            if (string.IsNullOrEmpty(name)) return false;

            if (HasData(name))
            {
                _datas.Remove(name);
                return true;
            }

            return false;
        }
        #endregion
    }
}

